package dan.britt.hash;

import java.lang.reflect.Array;

@SuppressWarnings({ "unchecked" })
public class MyMap<K, V> {
	//some arbitrary constants
	private int ARRAY_SIZE_SMALL = 318833;
	private int ARRAY_SIZE_MEDIUM = 721631;
	private int ARRAY_SIZE_LARGE = 1299269;

	private int initialLength; //init array len
	
	//following 4 are used to create generic arrays
	private	Object keyObj = new Object();
	private	Object valueObj = new Object();
	private Class<K> keyBuilder = (Class<K>) keyObj.getClass();
	private Class<V> valueBuilder = (Class<V>) valueObj.getClass();
	
	//These are the gen arrays that store keys and values
	private K[] hashKey;
	private	V[] hashValue;

	/**
	 * Basic constructor, uses ARRAY_SIZE for the size of the arrays
	 */
	MyMap() {
		initialLength = ARRAY_SIZE_SMALL;
		genSet(keyBuilder, valueBuilder);
	}

	/**
	 * Constuctor that includes the size of hash content
	 * @param init Inital size of incoming content
	 */
	MyMap(int init) {
		if (init < ARRAY_SIZE_SMALL)
			this.initialLength = ARRAY_SIZE_SMALL;
		else if (init < ARRAY_SIZE_MEDIUM)
			this.initialLength = ARRAY_SIZE_MEDIUM;
		else
			this.initialLength = ARRAY_SIZE_LARGE;
		genSet(keyBuilder, valueBuilder);
	}

	/**
	 * This fuction 
	 * @param key
	 * @param value
	 */
	private void genSet(Class<K> key, Class<V> value) {
		hashKey = (K[]) Array.newInstance(key, initialLength);
		hashValue = (V[]) Array.newInstance(value, initialLength);
	}

	/**
	 * 
	 * @param s
	 * @return
	 */
	int hashFunction(String s) {
		int location = 0;
		int key = 0;
		int probeDecrement = 0;
		
		for (int i = 0; i < s.length(); i++) {
			key += s.charAt(i);
		}

		key *= 2459;
		
		
		location = key %  hashValue.length;
		
		if (hashKey[location] != null ){
			probeDecrement = Math.max(1, key/hashValue.length);
			while (hashKey[location] != null && ! hashKey[location].equals(s)){
				location -= probeDecrement;
				if (location < 0)
					location = hashKey.length - 1;
			}
		}

		return location;	
	}

	/**
	 * 
	 * @param key
	 * @param value
	 */
	void put(K key, V value) {
		int ndx;
		ndx = hashFunction((String) key);
		hashKey[ndx] = key;
		hashValue[ndx] = value;
	}

//	/**
//	 * This method finds the ndx of a key.
//	 * @param key
//	 * @return integer location of key, -1 if not there
//	 */
//	int getNdx(K key) {
//		int ndx = hashFunction((String) key);
//		// if ndx yields a null, then not in map
//		if (hashValue[ndx] == null)
//			return -1;
//		else
//			// check to see that key matches hashKey array
//			do {// while
//				if (key.equals(hashKey[ndx]))
//					return ndx;
//				//while not equal, keep doing shit
//				while (! key.equals(hashKey[ndx]) && hashKey[ndx] != null) {
//					if (ndx >= hashKey.length)
//						ndx = 0;
//					else
//						ndx += 1;
//				}
//			} while (hashValue[ndx] != null);
//		return -1;
//	}
	
	/**
	 * This method retieves the value stored at a key
	 * @param key
	 * @return the value of a key, null if not there
	 */
	V get(K key){
		int ndx = hashFunction((String) key);
		return hashValue[ndx];
	}

	/**
	 * This method removes a key value pair if they exist in the hash
	 * @param key
	 * @return true if removed else return false
	 */
	boolean remove(K key) {
		int ndx = hashFunction((String) key);
		if (hashKey[ndx] == null)
			return false;
		else{
			hashKey[ndx] = null;
			hashValue[ndx] = null;
		}
		return true;
	}
	
	/**
	 * Uhh, Duh! Muthafucka!!
	 */
	void clear (){
		genSet(keyBuilder, valueBuilder);
	}

	/**
	 * This method checks to see if a key is in the hash
	 * @param key
	 * @return True if key is in hash otherwise false
	 */
	boolean containsKey(K key){
		int ndx = hashFunction((String) key);
		if (hashKey[ndx] == null)
				return false;
		return true;
	}
	
	/**
	 * Checks to see if a value is contained in the list
	 * @param value
	 * @return true if value is there, else falsed
	 */
	boolean containsValue(V value){
		for(V val : hashValue)
			if (val.equals(value))
				return true;
		return false;
	}
	
	
//	Set keySet(){
//		
//	}
}
